﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensibility;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Syntax;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Execution;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Reporting;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap
{
    /// <summary>
    /// Abstract bootstrapping strategy definition.
    /// </summary>
    /// <typeparam name="TExtension">The type of the extension.</typeparam>
    public abstract class AbstractBootstrappingStrategy<TExtension> : IBootstrappingStrategy<TExtension>
        where TExtension : IBootstrappingExtension
    {
        #region Fields

        private readonly ISyntaxBuilder<TExtension> runSyntaxBuilder;
        private readonly ISyntaxBuilder<TExtension> shutdownSyntaxBuilder;
        private readonly ISyntaxBuilder<TExtension> configureSyntaxBuilder;

        private bool runSyntaxBuilded;
        private bool shutdownSyntaxBuilded;
        private bool configureSyntaxBuilded;

        #endregion

        #region Constructors

        protected AbstractBootstrappingStrategy()
            : this(
                new SyntaxBuilder<TExtension>(new ExecutableFactory<TExtension>()),
                new SyntaxBuilder<TExtension>(new ExecutableFactory<TExtension>()),
                new SyntaxBuilder<TExtension>(new ExecutableFactory<TExtension>()))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractStrategy&lt;TExtension&gt;"/> class.
        /// </summary>
        /// <param name="runSyntaxBuilder">The run syntax builder.</param>
        /// <param name="shutdownSyntaxBuilder">The shutdown syntax builder.</param>
        protected AbstractBootstrappingStrategy(
            ISyntaxBuilder<TExtension> runSyntaxBuilder,
            ISyntaxBuilder<TExtension> shutdownSyntaxBuilder,
            ISyntaxBuilder<TExtension> configureSyntaxBuilder)
        {
            this.shutdownSyntaxBuilder = shutdownSyntaxBuilder;
            this.runSyntaxBuilder = runSyntaxBuilder;
            this.configureSyntaxBuilder = configureSyntaxBuilder;
        }

        #endregion

        #region Dispose Logic

        /// <summary>
        /// Finalizes an instance of the <see cref="AbstractStrategy{TExtension}"/> class.
        /// </summary>
        ~AbstractBootstrappingStrategy()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is disposed.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
        /// </value>
        protected bool IsDisposed { get; private set; }

        public void Dispose()
        {
            this.Dispose(true);

            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.IsDisposed && disposing)
            {
                this.IsDisposed = true;
            }
        }

        #endregion

        #region IBootstrappingStrategy<TExtension> Implementations

        /// <inheritdoc />
        /// <remarks>By default creates a SynchronousExecutor{TExtension}</remarks>
        public virtual IExecutor<TExtension> CreateRunExecutor()
        {
            return new SynchronousExecutor<TExtension>();
        }

        /// <inheritdoc />
        /// <remarks>By default creates a SynchronousReverseExecutor{TExtension}</remarks>
        public virtual IExecutor<TExtension> CreateShutdownExecutor()
        {
            return new SynchronousReverseExecutor<TExtension>();
        }

        public virtual IExecutor<TExtension> CreateConfigureExecutor()
        {
            return new SynchronousExecutor<TExtension>();
        }

        /// <remarks>By default creates a NullExtensionResolver{TExtension}</remarks>
        public virtual IBootstrappingExtensionResolver<TExtension> CreateExtensionResolver()
        {
            return new NullBootstrappingExtensionResolver<TExtension>();
        }

        /// <remarks>By default creates a ReportingContext</remarks>
        public virtual IReportingContext CreateReportingContext()
        {
            return new ReportingContext();
        }


        public ISyntax<TExtension> BuildRunSyntax()
        {
            this.CheckRunSyntaxNotAlreadyBuilt();

            this.DefineRunSyntax(this.runSyntaxBuilder);

            return this.runSyntaxBuilder;
        }

        public ISyntax<TExtension> BuildShutdownSyntax()
        {
            this.CheckShutdownSyntaxNotAlreadyBuilt();

            this.DefineShutdownSyntax(this.shutdownSyntaxBuilder);

            return this.shutdownSyntaxBuilder;
        }

        public ISyntax<TExtension> BuildConfigureSyntax()
        {
            this.CheckConfigureSyntaxNotAlreadyBuilt();

            this.DefineConfigureSyntax(this.configureSyntaxBuilder);

            return this.configureSyntaxBuilder;
        }

        #endregion

        #region Abstract  Methods/ Extension points for subclasses

        /// <summary>
        /// Fluently defines the run syntax on the specified builder.
        /// </summary>
        /// <param name="builder">The syntax builder</param>
        protected abstract void DefineRunSyntax(ISyntaxBuilder<TExtension> builder);

        /// <summary>
        /// Fluently defines the shutdown syntax on the specified builder.
        /// </summary>
        /// <param name="builder">The syntax builder</param>
        protected abstract void DefineShutdownSyntax(ISyntaxBuilder<TExtension> builder);

        /// <summary>
        /// Fluently defines the configure syntax on the specified builder.
        /// </summary>
        /// <param name="builder">The syntax builder</param>
        protected abstract void DefineConfigureSyntax(ISyntaxBuilder<TExtension> builder);

        #endregion

        #region Private Methods

        private void CheckRunSyntaxNotAlreadyBuilt()
        {
            if (this.runSyntaxBuilded)
            {
                throw new BootstrapperException("The run syntax can only be acquired once.");
            }

            this.runSyntaxBuilded = true;
        }

        private void CheckShutdownSyntaxNotAlreadyBuilt()
        {
            if (this.shutdownSyntaxBuilded)
            {
                throw new BootstrapperException("The shutdown syntax can only be acquired once.");
            }

            this.shutdownSyntaxBuilded = true;
        }

        private void CheckConfigureSyntaxNotAlreadyBuilt()
        {
            if (this.configureSyntaxBuilded)
            {
                throw new BootstrapperException("The configure syntax can only be acquired once.");
            }

            this.configureSyntaxBuilded = true;
        }

        #endregion
    }
}
